<?php
/**
 *
 * 学习与实验代码
 * 可以一个个打开
 * 调试
 *
 * Created by PhpStorm.
 * User: zhezhao
 * Date: 2016/10/20
 * Time: 10:51
 */
//  1 多进程操作时，进程通信出现数据混乱！


// 多进程操作时，为何调用无意义的管道操作！,实验证明管道操作并不影响进程的执行流程，只是给出功能，按需使用
/*$worker_num = 6;
$workers = [];
$data = [
    'worker1', 'worker2', 'worker3', 'worker4', 'worker5', 'worker6',
    'worker7', 'worker8', 'worker9', 'worker10', 'worker11', 'worker12',
    'worker13', 'worker14', 'worker15', 'worker16', 'worker17', 'worker18',
    'worker19', 'worker20', 'worker31', 'worker32', 'worker33', 'worker34',
    'worker35', 'worker36', 'worker37', 'worke3r8', 'worker39', 'worker40',
    'worker41', 'worker42', 'worker43',
];

echo "start:" . date("Y-m-d H:i:s", time());
$dispatch = dispatch($worker_num, $data);
for ($i = 0; $i < $worker_num; $i++) {
    $the_dispatch = $dispatch[$i];
    $process = new swoole_process(function (swoole_process $worker) use($the_dispatch) {
        deal($the_dispatch);
    }, false);
    $pid = $process->start();
    $workers[$pid] = $process;
}


function deal($task) {
    foreach ($task as $item) {
        echo $item . PHP_EOL;
        sleep(1);
    }
}

swoole_process::signal(SIGCHLD, function($sig) {
    //必须为false，非阻塞模式
    while($ret =  swoole_process::wait(false)) {
        echo "PID={$ret['pid']}\n";
    }
});


foreach ($workers as $pid => $worker) {
    if ($ret = $worker->wait(true)) {
        echo "PID={$ret['pid']} || $pid \n";
    } else {
        exit("ma zha la{$ret['pid']} \n");
    }
}

echo "end:" . date("Y-m-d H:i:s", time());*/

// 简单的任务进程调度分配
/**
 * 按公约数先平均分配，多余的任务在依次分配
 *
 * @param $worker_num
 * @param array $tasks 任务队列、数组
 */
function dispatch($worker_num, $tasks) {
    //$in = ceil($task_num/$worker_num);
    $dispatch = [];// 分配的结果数组，该数组以自然下标对应workers进程数组的自然下标，值为对应的分配的任务队列
    $task_num = count($tasks);
    $avg = floor($task_num/$worker_num);
    $remainder = $task_num - $avg * $worker_num;
    switch ($remainder) {
        case 0:
            for ($i=0; $i<$worker_num; $i++) {
                if ($i == 0) {
                    $dispatch[$i] = array_slice($tasks, 0, $avg);
                } else {
                    $dispatch[$i] = array_slice($tasks, ($avg)*($i), $avg);
                }
            }
            break;

        default:
            for ($i=0; $i<$worker_num; $i++) {
                if ($i == 0) {
                    $dispatch[$i] = array_slice($tasks, 0, $avg+1);
                } else if($i >= $remainder) {
                    $dispatch[$i] = array_slice($tasks, $remainder*($avg+1)+($i-$remainder)*$avg, $avg);
                } else {
                    $dispatch[$i] = array_slice($tasks, ($avg+1)*($i), $avg+1);
                }
            }
            break;
    }

    return $dispatch;

}

/*  2 // table 实验
$table = new swoole_table(1024);
$table->column('name', swoole_table::TYPE_STRING, 64);
$table->column('age', swoole_table::TYPE_INT, 4);
$table->column('sex', swoole_table::TYPE_STRING, 4);
if (!$table->create()) {
    echo "申请内存失败！\n";
    exit(0);
}
$table->set('mmp', ['name' => 'ppx', 'age' => 123, 'sex' =>  'nv']);
$res = $table->get('mmp');
$table->set('mmp1', ['age' => 232]);
$string = json_encode(['code' => 1, 'ppt' => 'o>mygod', 'created_time' =>'1998-23-42 23:09:04']);
$table['mmp3'] = ['name' => $string];
var_dump(json_decode($table['mmp3']['name']));*/

// 异步tcp服务器
/** tcp 在处理事件中用到异步方式处理
 * 异步tcp两个重要函数
 * task函数投递异步任务finish函数任务完成后的结果
 */
/*  3 $serv = new swoole_server('0.0.0.0', 9502);
// 设置异步 进程工作数量
$serv->set(['task_worker_num' => 4]);
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
    $task_id = $serv->task($data);// 异步task_id
    echo "异步id：$task_id \n";
});

// 处理异步任务
$serv->on('task', function ($serv, $task_id, $from_id, $data) {
    echo "执行异步ID： $task_id \n";
    $serv->finish("$data -> OK");
});

$serv->on('finish', function($serv, $task_id, $data) {
    echo "执行完成\n";
});

$serv->start();*/

//  4 http服务器
/*$serv = new swoole_http_server('0.0.0.0', 9502);
$serv->on('connect', function($serv, $fd){
    echo "建立连接\n";
    var_dump($serv, $fd);
});

$serv->on('request', function($request, $response){
    echo "接收到的数据\n";
    var_dump($request);
    $response->header("Content-Type", "text/html; charset=utf-8");
    $response->end("hello world" . rand(100,999));
});

$serv->start();*/

/*  5 // 创建tcp服务器
$serv = new swoole_server('0.0.0.0', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);
$serv->on('connect', function($serv, $fd){
    echo "建立连接\n";
    var_dump($serv, $fd);
});

$serv->on('receive', function($serv, $fd, $data, $request){
    echo "接收到的数据\n";
    var_dump($request);
});

$serv->on('close', function($serv, $fd) {
   echo "finish";
});

$serv->start();*/
// 6 异步事件
/*$fp = stream_socket_client("tcp://www.qq.com:80",$errno, $errstr, 30);
fwrite($fp, "GET / HTTP/1.1\r\nHost: 222.qq.com\r\n\r\n");
// 添加异步事件
swoole_event_add($fp, function($fp) {
    $resp = fread($fp, 8192);
    var_dump($resp);
    swoole_event_del($fp);
    fclose($fp);
});
echo "这个先完成！\n";*/
/*// 8 dns查询
swoole_async_dns_lookup("www.baidu.com", function ($host, $ip) {
    echo "$host  \n  $ip\n";
});*/

/*//7 创建进程
$process = new swoole_process("doProcess", true);
$pid = $process->start();
// 进程对应的执行函数
function doProcess(swoole_process $process) {
    echo "pid : $process->pid \n";
    sleep(10);
}


// 等待结束
swoole_process::wait();*/

//6 锁机制深入理解
/*$lock = new swoole_lock(SWOOLE_MUTEX);//互斥锁
echo "创建互斥锁 \n";
$pid = pcntl_fork();
//$pid2 = pcntl_fork();
if ($pid > 0 ) {
    $lock->lock();//开始锁定主进程
    echo "主进程枷锁休息30s\n";
    sleep(30);
    $lock->unlock();//解锁
    echo "主进程释放锁\n";
} else{
    echo "子进程走这里\n";

    echo "子进程 申请锁\n";
    $lock->lock();
    sleep(20);
    echo "子进程 获取到锁 休息20s\n";
    //exit('子进程退出');
}

if ($pid) {
    $lock->unlock();
    echo "主进程竟然能够/ 释放子进程的锁\n";
} else {
    $lock->unlock();
    echo "子进程/ 释放锁\n";
}

if ($pid) {
    echo "主进程先走啦\n";
} else {
    echo "子进程先走啦\n";
}*/


//unset($lock);

// 5 锁机制
// 创建锁的对象
/*$lock = new swoole_lock(SWOOLE_MUTEX);//互斥锁
echo "创建互斥锁 \n";
$lock->lock();//开始锁定主进程
if (pcntl_fork() > 0 ) {
    sleep(1);
    $lock->unlock();//解锁
} else{
    echo "子进程 等待锁\n";
    $lock->lock();// 上锁
    echo "子进程 获取锁\n";
    $lock->unlock();// 释放锁
    exit('子进程退出');
}
echo "主进程 释放锁\n";
unset($lock);
echo "子进程退出";*/



//4 进程触发 异步执行 达到一定条件停止
/*swoole_process::signal(SIGALRM, function() {
    static $i =0;
    echo "$i\n";
    $i++;
    if ($i>10) {
        swoole_process::alarm(-1);
    }
});

// 定时信号
swoole_process::alarm(100*1000);// 微秒级别*/

// 3 实验，进程的阻塞模式消息队列能否保证一对一通信
/*$workers = [];
$worker_num =3;
$message = [];


// 批量创建
for($i= 0; $i < $worker_num; $i++) {
    $process = new swoole_process(function(swoole_process $process) use($i) {
        $recv = $process->pop(); // 获取数据 ，默认8192
        echo "从主进程获取来自【{$i}】的数据： $recv \n";
        sleep(2);
        $process->exit(0);
    }, false, false);// 第三个参数false，进程通信
    $process->useQueue(1, 2 | swoole_process::IPC_NOWAIT); // 开启队列，类似于全局函数
    $pid = $process->start();
    $workers[$i] = $process;
    $message[$i] = '我是进程['. $pid . '-' . "$i]发送的消息";

}
// 主进程向子进程添加数据
foreach ($workers as $i => $process) {
    //$process->freeQueue();
    $process->push($message[$i]);
}

// 等待子进程结束 回收资源
for($i= 0; $i< $worker_num; $i++) {
    $ret = swoole_process::wait();// 等待执行完成；

    $pid = $ret['pid'];
    unset($workers[$pid]);
    echo "finish : $pid \n";
}*/
/*function doProcess(swoole_process $process) {
    $recv = $process->pop(); // 获取数据 ，默认8192
    echo "从主进程获取的数据： $recv \n";
    sleep(2);
    $process->exit(0);
}*/


// 2
/*for($i= 0; $i < $worker_num; $i++) {
    $process = new swoole_process('doProcess');
    $pid = $process->start();
    $workers[$pid] = $process;
}

function doProcess(swoole_process $process) {
    $process->write('PID:' . $process->pid);
    echo 'msg:' . $process->pid . $process->callback;
}

// 添加进程事件，向每一个子进程添加需要执行的动作
foreach ($workers as $pid => $process) {
    swoole_event_add($process->pipe, function($pipe)use($process){// 资源句柄,问题是加这个鬼有什么用，不加一样有这个输出
        $data= $process->read();
        echo '接收到：' . $data . '\n';
    });
}*/


//1
/*$url_arr = array();
for ($i=0;$i<10;$i++){
    $url_arr[] = "www.baidu.com?wd=".$i;
}

echo "start:".date("Y-m-d H:i:s").PHP_EOL;
$workers = array();
for ($i=0;$i<5;$i++){
    $process = new swoole_process('getContents',true);
    $pid = $process->start();
    $process->write($i);
    $workers[$pid] = $process;
}

//主进程数据结果
foreach ($workers as $process){
    echo $process->read();
    echo PHP_EOL;
}

echo "end:".date("Y-m-d H:i:s").PHP_EOL;
function getContents(swoole_process $worker){
    $i = $worker->read();
    global $url_arr;
    $res1 = execCurl($url_arr[($i*2)]);
    $res2 = execCurl($url_arr[($i*2+1)]);
    echo $res1.PHP_EOL.$res2;
}

function execCurl($url){
    sleep(2);
    return "handle ".$url." finished";
}*/